package top.rish.rib.base.ware;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.zhxu.bs.BeanSearcher;
import cn.zhxu.bs.SearchResult;
import lombok.extern.slf4j.Slf4j;
import org.beetl.sql.annotation.entity.Table;
import org.beetl.sql.mapper.BaseMapper;
import org.beetl.sql.solon.annotation.Db;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Result;
import org.noear.solon.i18n.I18nUtil;
import org.noear.solon.validation.BeanValidateInfo;
import org.noear.solon.validation.ValidatorManager;
import top.rish.rib.base.utils.StpUserUtil;

import java.io.IOException;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class
BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity>
        implements BaseService<T> {

    @Db
    protected M baseMapper;

    @Inject
    protected BeanSearcher beanSearcher;

    public SearchResult<T> page(Context ctx) {
        return this.page(ctx, this.baseMapper.getTargetEntity());
    }

    @Override
    public List<T> all(Context ctx) {
        Class<T> clazz = baseMapper.getTargetEntity();
        Map<String, Object> fatMap = this.pageQueryMap(ctx, clazz);
        return beanSearcher.searchAll(clazz,fatMap);
    }

    public <Y> SearchResult<Y> page(Context ctx, Class<Y> clazz) {
        Map<String, Object> fatMap = this.pageQueryMap(ctx, clazz);
        String[] summaryFields = this.pageQuerySummaryFields(ctx, clazz);
        SearchResult<Y> searchResult = beanSearcher.search(clazz, fatMap, summaryFields);
        if (ObjectUtil.isNotEmpty(searchResult) && ObjectUtil.isNotEmpty(searchResult.getDataList())) {
            List<Y> oldDataList = searchResult.getDataList();
            if (ObjectUtil.isNotEmpty(oldDataList)) {
                List<Y> newDataList = this.pageResultParse(oldDataList);
                if (ObjectUtil.isNotEmpty(newDataList)) {
                    ReflectUtil.setFieldValue(searchResult, "dataList", newDataList);
                }
            }
        }
        return searchResult;
    }

    public <Y> List<Y> pageResultParse(List<Y> dataList) {
        return null;
    }

    public String[] pageQuerySummaryFields(Context ctx, Class<?> forClass) {
        String[] summaryFields = null;
        if (ctx != null) {
            Map<String, List<String>> paramsMap = ctx.paramsMap();
            if (paramsMap != null && !paramsMap.isEmpty()) {
                List<String> suMap = paramsMap.get("summaryFields");
                if (suMap != null && !suMap.isEmpty()) {
                    summaryFields = suMap.toArray(new String[0]);
                }
            }
        }
        return summaryFields;
    }

    public Map<String, Object> pageQueryMap(Context ctx, Class<?> forClass) {
        Map<String, Object> fatMap = new HashMap<>();
        if (ctx != null) {
            Map<String, List<String>> paramsMap = ctx.paramsMap();
            for (Map.Entry<String, List<String>> entry : paramsMap.entrySet()) {
                String key = entry.getKey();
                List<String> values = entry.getValue();
                values = values.stream().filter(ObjectUtil::isNotEmpty).toList();
                if (values.size() == 1) {
                    fatMap.put(key, values.get(0));
                } else if (values.size() >= 2) {
                    fatMap.put(key, values);
                }
            }
        }
        String orderBy = this.pageQueryOrderBy();
        if (ObjectUtil.isNotEmpty(orderBy)) {
            fatMap.put("orderBy", orderBy);
        }
        return fatMap;
    }

    protected String pageQueryOrderBy() {
        return "sort:desc";
    }

    @Override
    public T add(T entity) {
        if (entity == null) {
            throw new RuntimeException("参数不能为空，新增失败");
        }
        entity.setCreatedBy(StpUserUtil.adminStp.getLoginIdAsString());
        entity.setUpdatedBy(StpUserUtil.adminStp.getLoginIdAsString());
        String tableName = tableName();
        if (tableName == null) {
            throw new RuntimeException("新增失败");
        }
        if(ObjectUtil.isNotEmpty(entity.getParentId()) && !"0".equals(entity.getParentId()) ) {
            T p =  this.getById(entity.getParentId());
            if(p == null) {
                throw new RuntimeException("新增失败，父级数据不存在");
            }
        }
        if(ObjectUtil.isEmpty(entity.getParentId())  ) {
            entity.setParentId("0");
        }
        entity.setId(null);
        entity.setDeleted(0L);
        entity.setCreatedAt(LocalDateTime.now());
        entity.setUpdatedAt(entity.getCreatedAt());
        List<T> maxSort = this.baseMapper.execute(String.format("select sort from %s order by sort desc limit 1", tableName));
        if (!maxSort.isEmpty()) {
            entity.setSort(maxSort.get(0).getSort() + 1);
        }
        int insert = this.baseMapper.insertTemplate(entity);
        if (insert == 1) {
            return this.getById(entity.getId());
        } else {
            throw new RuntimeException("新增失败，请重新尝试");
        }
    }

    @Override
    public T updateById(T entity) {
        if (entity == null) {
            throw new RuntimeException("参数不能为空，更新失败");
        }
        entity.setUpdatedBy(StpUserUtil.adminStp.getLoginIdAsString());
        T inDb = this.getById(entity.getId());
        if (inDb == null || ObjectUtil.equal(1, inDb.getDeleted())) {
            throw new RuntimeException("数据不存在");
        }
        if (ObjectUtil.isEmpty(entity.getUpdatedAt())) {
            throw new RuntimeException("数据不完整，更新失败");
        }
        if (!ObjectUtil.equals(entity.getUpdatedAt(), inDb.getUpdatedAt())) {
            throw new RuntimeException("数据已经被其他用户更新，请重新操作");
        }
        int update = this.baseMapper.updateById(entity);
        if (update == 1) {
            return this.getById(entity.getId());
        } else {
            throw new RuntimeException("更新失败，请重新尝试");
        }
    }

    public String tableName() {
        Table t = this.baseMapper.getTargetEntity().getAnnotation(Table.class);
        if (t == null || t.name() == null || t.name().trim().isEmpty()) {
            return null;
        }
        return t.name();
    }

    @Override
    public T deleteById(T entity) {
        if (entity == null) {
            throw new RuntimeException("参数不能为空，删除失败");
        }
        return this.deleteById(entity.getId());
    }

    @Override
    public T deleteById(Serializable id) {
        if (id == null) {
            throw new RuntimeException("参数不能为空，删除失败");
        }
        String tableName = tableName();
        if (tableName == null) {
            throw new RuntimeException("删除失败");
        }
        T dataInDb = this.getById(id);
        if (dataInDb == null) {
            throw new RuntimeException("删除失败，数据不存在或已删除");
        }
        dataInDb.setDeleted(1L);
        dataInDb.setDeletedAt(LocalDateTime.now());
        dataInDb.setDeletedBy(StpUserUtil.adminStp.getLoginIdAsString());
        return this.updateById(dataInDb);

    }

    @Override
    public T getById(Serializable id) {
        return this.baseMapper.single(id);
    }

    @Override
    public T add(Context ctx) {
        T t = bodyJson(ctx);
        if (t == null) {
            throw new RuntimeException("参数不能为空，新增失败");
        }
        return this.add(t);
    }

    @Override
    public boolean batchAdd(Context ctx) {
        ONode jsonData  = bodyJson(ctx,ONode.class);
        if(jsonData == null || jsonData.isNull()) {
            throw new RuntimeException("参数不能为空，新增失败");
        }
        if (!jsonData.contains("dataList")) {
            throw new RuntimeException("参数dataList缺失，新增失败");
        }
        ONode dataList = jsonData.get("dataList");
        if(!dataList.isArray()) {
            throw new RuntimeException("参数dataList必须是数组，新增失败");
        }
        List<T> maxSort = this.baseMapper.execute(String.format("select if(sort is null ,100,sort) as sort from %s order by sort desc limit 1", tableName()));
        AtomicReference<Long> sort = new AtomicReference<>(100L);
        if (!maxSort.isEmpty()) {
            sort.set(maxSort.get(0).getSort());
        }
        LocalDateTime now =  LocalDateTime.now();
        List<T> list = dataList.toObjectList(this.baseMapper.getTargetEntity());
        list =  list.stream().peek(entity->{
            entity.setCreatedBy(StpUserUtil.adminStp.getLoginIdAsString());
            entity.setUpdatedBy(StpUserUtil.adminStp.getLoginIdAsString());
            entity.setUpdatedAt(now);
            entity.setCreatedAt(now);
            entity.setDeleted(0L);
            entity.setStatus(1L);
            sort.set(sort.get() + 1L);
            entity.setSort(sort.get());
        }).toList();
        for (T t : list) {
            this.baseMapper.insertTemplate(t);
        }
        return  true;
    }

    @Override
    public T mod(Context ctx) {
        T t = bodyJson(ctx);
        if (t == null) {
            return null;
        }
        return this.updateById(t);
    }

    @Override
    public T del(Context ctx) {
        T t = bodyJson(ctx);
        if (t == null) {
            throw new RuntimeException("参数不能为空，删除失败");
        }
        return this.deleteById(t);
    }

    public String body(Context ctx) {
        try {
            return ctx.body();
        } catch (IOException e) {
            throw new RuntimeException("请求参数错误");
        }
    }


    public static String replaceBrace(String sql) {
        String pattern = "(\\$\\{(.+?)\\})";
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(sql);
        StringBuilder sb = new StringBuilder();
        while (m.find()) {
            String oldKey = m.group();
            if (oldKey != null && oldKey.length() > 3) {
                String newKey = oldKey.replace("${", "").replace("}", "");
                String msg = I18nUtil.getMessage(newKey);
                m.appendReplacement(sb, msg);
            }
        }
        m.appendTail(sb);
        return sb.toString();
    }

    public <X> X validJson(Context ctx, Class<X> clazz, Class<?>... groupClass) {
        X r = this.bodyJson(ctx, clazz);
        Result rst = ValidatorManager.validateOfEntity(r, groupClass);
        if (rst.getCode() != Result.SUCCEED_CODE) {
            Object rdata = rst.getData();
            StringBuffer msg = new StringBuffer();
            if (rdata instanceof Iterable<?> d) {
                Class<?> c = IterUtil.getElementType(d);
                if (BeanValidateInfo.class.isAssignableFrom(c)) {
                    Iterator<BeanValidateInfo> iterator = (Iterator<BeanValidateInfo>) d.iterator();
                    while (iterator.hasNext()) {
                        BeanValidateInfo info = iterator.next();
                        Object message = ReflectUtil.getFieldValue(info, "message");
                        msg.append(replaceBrace(message + ""));
                        msg.append(",");
                    }

                }
            } else if (rdata instanceof BeanValidateInfo) {
                BeanValidateInfo info = (BeanValidateInfo) rdata;
                Object message = ReflectUtil.getFieldValue(info, "message");
                msg.append(replaceBrace(message + ""));
            }
            if(!msg.isEmpty() && msg.charAt(msg.length()-1) == ',') {
                msg.deleteCharAt(msg.length() - 1);
            }
            if (msg.isEmpty()) {
                throw new RuntimeException("参数异常");
            } else {
                throw new RuntimeException(msg.toString());
            }
        }
        return r;
    }

    public <X> X bodyJson(Context ctx, Class<X>... clazz) {
        ONode oNode = ONode.load(body(ctx));
        if (oNode == null || oNode.isNull() ||  ObjectUtil.isEmpty(oNode.obj())
        ) {
            throw new RuntimeException("请求参数不能为空！");
        } else if (!oNode.isObject()) {
            throw new RuntimeException("请求参数不正确！");
        } else {
            Class<?> clz;
            if (clazz != null && clazz.length >= 1 && clazz[0] != null) {
                clz = clazz[0];
            } else {
                clz = this.baseMapper.getTargetEntity();
            }
            return oNode.toObject(clz);
        }
    }

    @Override
    public T get(Context ctx) {
        T t = bodyJson(ctx);
        if (t == null) {
            return null;
        }
        return this.getById(t.getId());
    }


    @Override
    public T changeStatus(Context ctx) {
        T t = bodyJson(ctx);
        if (t == null) {
            return null;
        }

        return t;
    }

    @Override
    public boolean sort(Context ctx) {
        ONode node = bodyJson(ctx, ONode.class);
        if (node == null) {
            return false;
        }
        String tableName = tableName();
        if (tableName == null) {
            throw new RuntimeException("排序失败");
        }
        String dragId = node.get("dragId").val().getString();
        String pressId = node.get("pressId").val().getString();
        if (ObjectUtil.isAllNotEmpty(dragId, pressId)) {
            if (ObjectUtil.equals(dragId, pressId)) {
                throw new RuntimeException("无效的拖拽，拖拽排序失败");
            }
            List<T> res = this.baseMapper.execute(String.format("select id,sort from %s where id in(?,?) and deleted = 0", tableName), dragId, pressId);
            if (res.size() != 2) {
                throw new RuntimeException("数据不存在，拖拽排序失败");
            }
            boolean sortIsEmpty = res.stream().anyMatch(it -> ObjectUtil.isEmpty(it.getSort()));
            if (sortIsEmpty) {
                throw new RuntimeException("该数据不支持拖拽，拖拽排序失败");
            }
            T dragEntity = res.stream().filter(it -> ObjectUtil.equals(dragId, it.getId())).findFirst().get();
            T pressEntity = res.stream().filter(it -> ObjectUtil.equals(pressId, it.getId())).findFirst().get();
            Long dragSort = dragEntity.getSort();
            Long pressSort = pressEntity.getSort();
            if (ObjectUtil.equals(dragSort, pressSort)) {
                throw new RuntimeException("无效的拖拽，拖拽排序失败");
            }
            if(Math.abs(dragSort - pressSort) > 100) {
                throw new RuntimeException("无效的拖拽，拖拽太远");
            }
            if (dragSort > pressSort) {
                // 往下拖拽,中间部分往上挪
                int i = this.baseMapper.executeUpdate(String.format("update %s set sort=sort+1 where sort >= ? and sort<? and deleted = 0", tableName), pressSort, dragSort);
                log.info("往下拖拽,更新了{}个", i);
            } else {
                // 往上拖拽,中间部分往下挪
                int i = this.baseMapper.executeUpdate(String.format("update %s set sort=sort-1 where sort > ? and sort<=? and deleted = 0", tableName), dragSort, pressSort);
                log.info("往上拖拽,更新了{}个", i);
            }
            this.baseMapper.executeUpdate(String.format("update %s set sort=? where id=?", tableName), pressSort, dragEntity.getId());
            return true;
        } else {
            throw new RuntimeException("拖拽排序失败");
        }
    }


}
